
package main;

import javax.microedition.lcdui.Image;

public class Resizer {
    // fixed point constants

    private static final int FP_SHIFT = 13;
    private static final int FP_ONE = 1 << FP_SHIFT;
    private static final int FP_HALF = 1 << (FP_SHIFT - 1);
    // resampling modes - valid values for the mode parameter of resizeImage()
    // any other value will default to MODE_BOX_FILTER because of the way the conditionals are set in resizeImage()
    public static final int MODE_POINT_SAMPLE = 0;
    public static final int MODE_BOX_FILTER = 1;

    /**
     * getPixels
     * Wrapper for pixel grabbing techniques.
     * I separated this step into it's own function so that other APIs (Nokia, Motorola, Siemens, etc.) can
     * easily substitute the MIDP 2.0 API (Image.getRGB()).
     * @param src The source image whose pixels we are grabbing.
     * @return An int array containing the pixels in 32 bit ARGB format.
     */
    public static int[] getPixels(Image src) {
        int w = src.getWidth();
        int h = src.getHeight();
        int[] pixels = new int[w * h];
        src.getRGB(pixels, 0, w, 0, 0, w, h);
        return pixels;
    }

    /**
     * drawPixels
     * Wrapper for pixel drawing function.
     * I separated this step into it's own function so that other APIs (Nokia, Motorola, Siemens, etc.) can
     * easily substitute the MIDP 2.0 API (Image.createRGBImage()).
     * @param pixels int array containing the pixels in 32 bit ARGB format.
     * @param w The width of the image to be created.
     * @param h The height of the image to be created. This parameter is actually superfluous, because it
     * must equal pixels.length / w.
     * @return The image created from the pixel array.
     */
    public static Image drawPixels(int[] pixels, int w, int h) {
        return Image.createRGBImage(pixels, w, h, true);
    }

    public static Image resizeImage(Image src, float factor) {
        return resizeImage(src, factor, MODE_BOX_FILTER);
    }

    /**
     * resizeImage
     * Gets a source image along with new size for it and resizes it.
     * @param src The source image.
     * @param destW The new width for the destination image.
     * @param destH The new heigth for the destination image.
     * @param mode A flag indicating what type of resizing we want to do. It currently supports two type:
     * MODE_POINT_SAMPLE - point sampled resizing, and MODE_BOX_FILTER - box filtered resizing (default).
     * @return The resized image.
     */
    public static Image resizeImage(Image src, float factor, int mode) {
        int srcW = src.getWidth();
        int srcH = src.getHeight();
        int destW = (int) (srcW * factor);
        int destH = (int) (srcH * factor);

        // create pixel arrays
        int[] destPixels = new int[destW * destH]; // array to hold destination pixels
        int[] srcPixels = getPixels(src); // array with source's pixels

        if (mode == MODE_POINT_SAMPLE) {
            // simple point smapled resizing
            // loop through the destination pixels, find the matching pixel on the source and use that
            for (int destY = 0; destY < destH; ++destY) {
                for (int destX = 0; destX < destW; ++destX) {
                    int srcX = (destX * srcW) / destW;
                    int srcY = (destY * srcH) / destH;
                    destPixels[destX + destY * destW] = srcPixels[srcX + srcY * srcW];
                }
            }
        } else {
            // precalculate src/dest ratios
            int ratioW = (srcW << FP_SHIFT) / destW;
            int ratioH = (srcH << FP_SHIFT) / destH;

            int[] tmpPixels = new int[destW * srcH]; // temporary buffer for the horizontal resampling step

            // variables to perform additive blending
            int argb; // color extracted from source
            int a, r, g, b; // separate channels of the color
            int count; // number of pixels sampled for calculating the average

            // the resampling will be separated into 2 steps for simplicity
            // the first step will keep the same height and just stretch the picture horizontally
            // the second step will take the intermediate result and stretch it vertically

            // horizontal resampling
            for (int y = 0; y < srcH; ++y) {
                for (int destX = 0; destX < destW; ++destX) {
                    count = 0;
                    a = 0;
                    r = 0;
                    b = 0;
                    g = 0; // initialize color blending vars
                    int srcX = (destX * ratioW) >> FP_SHIFT; // calculate beginning of sample
                    int srcX2 = ((destX + 1) * ratioW) >> FP_SHIFT; // calculate end of sample

                    // now loop from srcX to srcX2 and add up the values for each channel
                    do {
                        argb = srcPixels[srcX + y * srcW];
                        a += ((argb & 0xff000000) >> 24); // alpha channel
                        r += ((argb & 0x00ff0000) >> 16); // red channel
                        g += ((argb & 0x0000ff00) >> 8); // green channel
                        b += (argb & 0x000000ff); // blue channel
                        ++count; // count the pixel
                        ++srcX; // move on to the next pixel
                    } while (srcX <= srcX2 && srcX + y * srcW < srcPixels.length);

                    // average out the channel values
                    a /= count;
                    r /= count;
                    g /= count;
                    b /= count;

                    // recreate color from the averaged channels and place it into the temporary buffer
                    tmpPixels[destX + y * destW] = ((a << 24) | (r << 16) | (g << 8) | b);
                }
            }

            // vertical resampling of the temporary buffer (which has been horizontally resampled)
            for (int x = 0; x < destW; ++x) {
                for (int destY = 0; destY < destH; ++destY) {
                    count = 0;
                    a = 0;
                    r = 0;
                    b = 0;
                    g = 0; // initialize color blending vars
                    int srcY = (destY * ratioH) >> FP_SHIFT; // calculate beginning of sample
                    int srcY2 = ((destY + 1) * ratioH) >> FP_SHIFT; // calculate end of sample

                    // now loop from srcY to srcY2 and add up the values for each channel
                    do {
                        argb = tmpPixels[x + srcY * destW];
                        a += ((argb & 0xff000000) >> 24); // alpha channel
                        r += ((argb & 0x00ff0000) >> 16); // red channel
                        g += ((argb & 0x0000ff00) >> 8); // green channel
                        b += (argb & 0x000000ff); // blue channel
                        ++count; // count the pixel
                        ++srcY; // move on to the next pixel
                    } while (srcY <= srcY2 && x + srcY * destW < tmpPixels.length);

                    // average out the channel values
                    a /= count;
                    a = (a > 255) ? 255 : a;
                    r /= count;
                    r = (r > 255) ? 255 : r;
                    g /= count;
                    g = (g > 255) ? 255 : g;
                    b /= count;
                    b = (b > 255) ? 255 : b;

                    // recreate color from the averaged channels and place it into the destination buffer
                    destPixels[x + destY * destW] = ((a << 24) | (r << 16) | (g << 8) | b);
                }
            }
        }

        // return a new image created from the destination pixel buffer
        return drawPixels(destPixels, destW, destH);
    }
}
